{
  Copyright 2012 Sergey Ostanin

  Licensed under the Apache License, Version 2.0 (the "License");
  you may not use this file except in compliance with the License.
  You may obtain a copy of the License at

      http://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.
}

unit ProfileEditor;

interface

uses
  Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, ExtCtrls,
  StdCtrls, ComCtrls, ActnList, VisualUtils, Profile, MiscUtils,
  ModifierListEditor, TestCore, MarkScale;

type
  TProfileEditorForm = class(TForm)
    actEditModifiers: TAction;
    actOk: TAction;
    alMain: TActionList;
    btnOk: TButton;
    btnCancel: TButton;
    btnEditModifiers: TButton;
    cbLabelFilter: TCheckBox;
    cbShuffleQuestions: TCheckBox;
    cbEditableAnswers: TCheckBox;
    cbInstantAnswerCorrectness: TCheckBox;
    cbInstantTotalPercentCorrect: TCheckBox;
    cbBrowsableQuestions: TCheckBox;
    cbWeightCues: TCheckBox;
    cbScore: TCheckBox;
    cbPercentCorrect: TCheckBox;
    cbPoints: TCheckBox;
    cbMark: TCheckBox;
    cbQuestionScore: TCheckBox;
    cbSectionScore: TCheckBox;
    cbQuestionPercentCorrect: TCheckBox;
    cbQuestionCorrectAnswer: TCheckBox;
    cbQuestionPoints: TCheckBox;
    cbSectionPercentCorrect: TCheckBox;
    cbSectionPoints: TCheckBox;
    cbSectionQuestionCount: TCheckBox;
    cbSectionQuestionList: TCheckBox;
    edtTimeLimit: TEdit;
    edtLabelFilter: TEdit;
    edtQuestionsPerSection: TEdit;
    edtTitle: TEdit;
    gbxQuestionSelection: TGroupBox;
    gbxTimeLimit: TGroupBox;
    gbxTestProcess: TGroupBox;
    gbxScreenOptions: TGroupBox;
    gbxScore: TGroupBox;
    gbxQuestionScore: TGroupBox;
    gbxSectionScore: TGroupBox;
    lblTimeLimit: TLabel;
    lblQuestionsPerSection: TLabel;
    lblTitle: TLabel;
    pnlScore: TPanel;
    pgcMain: TPageControl;
    pnlTop: TPanel;
    pnlBottom: TPanel;
    rbTimeLimit: TRadioButton;
    rbNoTimeLimit: TRadioButton;
    rbQuestionsPerSection: TRadioButton;
    rbAllQuestions: TRadioButton;
    tsMarkScale: TTabSheet;
    tsScore: TTabSheet;
    tsSession: TTabSheet;
    procedure actEditModifiersExecute(Sender: TObject);
    procedure actOkExecute(Sender: TObject);
    procedure actOkUpdate(Sender: TObject);
    procedure cbEditableAnswersClick(Sender: TObject);
    procedure cbLabelFilterClick(Sender: TObject);
    procedure cbQuestionScoreClick(Sender: TObject);
    procedure cbScoreClick(Sender: TObject);
    procedure cbSectionScoreClick(Sender: TObject);
    procedure edtQuestionsPerSectionEnter(Sender: TObject);
    procedure edtTimeLimitEnter(Sender: TObject);
    procedure FormShow(Sender: TObject);
    procedure rbAllQuestionsClick(Sender: TObject);
    procedure rbNoTimeLimitClick(Sender: TObject);
    procedure rbQuestionsPerSectionClick(Sender: TObject);
    procedure rbTimeLimitClick(Sender: TObject);
  private
    FProfile: TProfile;
    FScaleEditor: TMarkScaleFrame;
    FSetOptimalSize: Boolean;
    procedure Prepare(Profile: TProfile; const OkCaption: String = '');
    procedure PrepareSessionPage;
    function SessionPageValid: Boolean;
    procedure SaveSessionPage;
    procedure PrepareScorePage;
    procedure SaveScorePage;
    procedure UpdateScoreEnabled;
    procedure PrepareMarkScalePage;
    { private declarations }
  public
    destructor Destroy; override;
    class function OpenModal(Profile: TProfile; const OkCaption: String = ''): Boolean;
    { public declarations }
  end;

implementation

{$R *.lfm}

procedure EnableCheckBoxes(const CheckBoxes: array of TCheckBox; Enable: Boolean);
var
  cb: TCheckBox;
begin
  for cb in CheckBoxes do
  begin
    cb.Enabled := Enable;
    if not Enable then
      SetCheckBoxStateWithoutOnClick(cb, FALSE);
  end;
end;

{ TProfileEditorForm }

procedure TProfileEditorForm.rbAllQuestionsClick(Sender: TObject);
begin
  SetEditColor(edtQuestionsPerSection, clBtnFace);
  edtQuestionsPerSection.TabStop := FALSE;
end;

procedure TProfileEditorForm.rbNoTimeLimitClick(Sender: TObject);
begin
  SetEditColor(edtTimeLimit, clBtnFace);
  edtTimeLimit.TabStop := FALSE;
end;

procedure TProfileEditorForm.edtQuestionsPerSectionEnter(Sender: TObject);
begin
  rbQuestionsPerSection.Checked := TRUE;
end;

procedure TProfileEditorForm.actOkUpdate(Sender: TObject);
begin
  (Sender as TAction).Enabled := SessionPageValid;
end;

procedure TProfileEditorForm.cbEditableAnswersClick(Sender: TObject);
begin
  if cbEditableAnswers.Checked then
  begin
    cbInstantAnswerCorrectness.Checked := FALSE;
    cbInstantAnswerCorrectness.Enabled := FALSE;
    cbInstantTotalPercentCorrect.Checked := FALSE;
    cbInstantTotalPercentCorrect.Enabled := FALSE;
  end
  else
  begin
    cbInstantAnswerCorrectness.Enabled := TRUE;
    cbInstantTotalPercentCorrect.Enabled := TRUE;
  end;
end;

procedure TProfileEditorForm.cbLabelFilterClick(Sender: TObject);
begin
  cbLabelFilter.Enabled := cbLabelFilter.Checked;
end;

procedure TProfileEditorForm.cbQuestionScoreClick(Sender: TObject);
begin
  UpdateScoreEnabled;
  if cbQuestionScore.Checked then
    cbQuestionPercentCorrect.Checked := TRUE;
end;

procedure TProfileEditorForm.cbScoreClick(Sender: TObject);
begin
  UpdateScoreEnabled;
  if cbScore.Checked then
  begin
    cbPercentCorrect.Checked := TRUE;
    cbMark.Checked := TRUE;
  end;
end;

procedure TProfileEditorForm.cbSectionScoreClick(Sender: TObject);
begin
  UpdateScoreEnabled;
  if cbSectionScore.Checked then
    cbSectionPercentCorrect.Checked := TRUE;
end;

procedure TProfileEditorForm.actOkExecute(Sender: TObject);
begin
  SaveSessionPage;
  SaveScorePage;

  ModalResult := mrOk;
end;

procedure TProfileEditorForm.actEditModifiersExecute(Sender: TObject);
var
  List: TModifierList;
begin
  List := FProfile.ModifierList.Clone;
  try
    if TModifierListEditorForm.OpenModal(List) then
      FProfile.ModifierList.Assign(List);
  finally
    List.Free;
  end;
end;

procedure TProfileEditorForm.edtTimeLimitEnter(Sender: TObject);
begin
  rbTimeLimit.Checked := TRUE;
end;

procedure TProfileEditorForm.FormShow(Sender: TObject);
begin
  if FSetOptimalSize then
  begin
    ConstrainMinPageSizeToLargest(pgcMain);
    AutoSize := TRUE;
    AutoSize := FALSE;
    RemoveMinPageSizeConstraints(pgcMain);
    Position := poScreenCenter;
  end;
end;

procedure TProfileEditorForm.rbQuestionsPerSectionClick(Sender: TObject);
begin
  SetEditColor(edtQuestionsPerSection, clDefault);
  edtQuestionsPerSection.TabStop := TRUE;
end;

procedure TProfileEditorForm.rbTimeLimitClick(Sender: TObject);
begin
  SetEditColor(edtTimeLimit, clDefault);
  edtTimeLimit.TabStop := TRUE;
end;

procedure TProfileEditorForm.Prepare(Profile: TProfile;
  const OkCaption: String = '');
begin
  FProfile := Profile;
  if OkCaption <> '' then
    actOk.Caption := OkCaption;

  HandleNeeded;

  StretchToWidest([btnOk, btnCancel]);
  SetEditWidthsByMaxLength([edtQuestionsPerSection, edtTimeLimit]);
  IncreaseMinHeight([rbAllQuestions, rbQuestionsPerSection, cbLabelFilter], 1);

  PrepareSessionPage;
  PrepareScorePage;
  PrepareMarkScalePage;
end;

procedure TProfileEditorForm.PrepareSessionPage;
var
  c: TWorkConfiguration;
begin
  edtTitle.Text := FProfile.Title;

  c := FProfile.Configuration;
  if c.QuestionsPerSection = 0 then
  begin
    SetRadioButtonStateWithoutOnClick(rbAllQuestions, TRUE);
    SetEditColor(edtQuestionsPerSection, clBtnFace);
    edtQuestionsPerSection.TabStop := FALSE;
  end
  else
  begin
    SetRadioButtonStateWithoutOnClick(rbQuestionsPerSection, TRUE);
    edtQuestionsPerSection.Text := IntToStr(c.QuestionsPerSection);
  end;

  SetCheckBoxStateWithoutOnClick(cbLabelFilter, c.UseLabelFilter);
  edtLabelFilter.Text := c.LabelFilter;

  { Labels are not supported currently. }
  cbLabelFilter.Enabled := c.UseLabelFilter;
  edtLabelFilter.ReadOnly := TRUE;
  SetEditColor(edtLabelFilter, clBtnFace);
  edtLabelFilter.TabStop := FALSE;

  cbShuffleQuestions.Checked := c.ShuffleQuestions;

  cbEditableAnswers.Checked := c.EditableAnswers;
  cbInstantAnswerCorrectness.Checked := c.InstantAnswerCorrectness;
  cbInstantTotalPercentCorrect.Checked := c.InstantTotalPercentCorrect;

  if c.DurationMinutes = 0 then
  begin
    SetRadioButtonStateWithoutOnClick(rbNoTimeLimit, TRUE);
    SetEditColor(edtTimeLimit, clBtnFace);
    edtTimeLimit.TabStop := FALSE;
  end
  else
  begin
    SetRadioButtonStateWithoutOnClick(rbTimeLimit, TRUE);
    edtTimeLimit.Text := IntToStr(c.DurationMinutes);
  end;

  cbBrowsableQuestions.Checked := c.BrowsableQuestions;
  cbWeightCues.Checked := c.WeightCues;
end;

function TProfileEditorForm.SessionPageValid: Boolean;
begin
  Result := TRUE;

  if Result and rbQuestionsPerSection.Checked then
    Result := StrToIntDef(edtQuestionsPerSection.Text, 0) > 0;

  if Result and rbTimeLimit.Checked then
    Result := StrToIntDef(edtTimeLimit.Text, 0) > 0;
end;

procedure TProfileEditorForm.SaveSessionPage;
var
  c: TWorkConfiguration;
begin
  FProfile.Title := CollapseSpaces(edtTitle.Text);

  c := FProfile.Configuration;
  if rbAllQuestions.Checked then
    c.QuestionsPerSection := 0
  else
    c.QuestionsPerSection := StrToInt(edtQuestionsPerSection.Text);

  c.UseLabelFilter := cbLabelFilter.Checked;
  if c.UseLabelFilter then
    c.LabelFilter := CollapseSpaces(edtLabelFilter.Text)
  else
    c.LabelFilter := '';

  c.ShuffleQuestions := cbShuffleQuestions.Checked;

  c.EditableAnswers := cbEditableAnswers.Checked;
  c.InstantAnswerCorrectness := cbInstantAnswerCorrectness.Checked;
  c.InstantTotalPercentCorrect := cbInstantTotalPercentCorrect.Checked;

  if rbNoTimeLimit.Checked then
    c.DurationMinutes := 0
  else
    c.DurationMinutes := StrToInt(edtTimeLimit.Text);

  c.BrowsableQuestions := cbBrowsableQuestions.Checked;
  c.WeightCues := cbWeightCues.Checked;
end;

procedure TProfileEditorForm.PrepareScorePage;
var
  p: TResultPolicy;
begin
  p := FProfile.ResultPolicy;

  SetCheckBoxStateWithoutOnClick(cbScore, p.ResultsAvailable);
  cbPercentCorrect.Checked := p.PercentCorrect;
  cbMark.Checked := p.Mark;
  cbPoints.Checked := p.Points;

  SetCheckBoxStateWithoutOnClick(cbQuestionScore, p.QuestionResultsAvailable);
  cbQuestionPercentCorrect.Checked := p.QuestionPercentCorrect;
  cbQuestionCorrectAnswer.Checked := p.QuestionCorrectAnswer;
  cbQuestionPoints.Checked := p.QuestionPoints;

  SetCheckBoxStateWithoutOnClick(cbSectionScore, p.SectionResultsAvailable);
  cbSectionPercentCorrect.Checked := p.SectionPercentCorrect;
  cbSectionPoints.Checked := p.SectionPoints;
  cbSectionQuestionCount.Checked := p.SectionQuestionCount;
  cbSectionQuestionList.Checked := p.SectionQuestionList;

  UpdateScoreEnabled;
end;

procedure TProfileEditorForm.SaveScorePage;
var
  p: TResultPolicy;
begin
  p := FProfile.ResultPolicy;

  p.ResultsAvailable := cbScore.Checked;
  p.PercentCorrect := cbPercentCorrect.Checked;
  p.Mark := cbMark.Checked;
  p.Points := cbPoints.Checked;

  p.QuestionResultsAvailable := cbQuestionScore.Checked;
  p.QuestionPercentCorrect := cbQuestionPercentCorrect.Checked;
  p.QuestionCorrectAnswer := cbQuestionCorrectAnswer.Checked;
  p.QuestionPoints := cbQuestionPoints.Checked;

  p.SectionResultsAvailable := cbSectionScore.Checked;
  p.SectionPercentCorrect := cbSectionPercentCorrect.Checked;
  p.SectionPoints := cbSectionPoints.Checked;
  p.SectionQuestionCount := cbSectionQuestionCount.Checked;
  p.SectionQuestionList := cbSectionQuestionList.Checked;
end;

procedure TProfileEditorForm.UpdateScoreEnabled;
begin
  EnableCheckBoxes([cbPercentCorrect, cbMark, cbPoints,
    cbQuestionScore, cbSectionScore], cbScore.Checked);

  EnableCheckBoxes([cbQuestionPercentCorrect, cbQuestionCorrectAnswer,
    cbQuestionPoints], cbQuestionScore.Checked);

  EnableCheckBoxes([cbSectionPercentCorrect, cbSectionPoints,
    cbSectionQuestionCount], cbSectionScore.Checked);

  EnableCheckBoxes(cbSectionQuestionList,
    cbQuestionScore.Checked and cbSectionScore.Checked);
end;

procedure TProfileEditorForm.PrepareMarkScalePage;
begin
  FScaleEditor := TMarkScaleFrame.Create(nil);
  FScaleEditor.SetScale(FProfile.MarkScale);
  FScaleEditor.Parent := tsMarkScale;
end;

destructor TProfileEditorForm.Destroy;
begin
  FreeAndNil(FScaleEditor);
  inherited;
end;

class function TProfileEditorForm.OpenModal(Profile: TProfile;
  const OkCaption: String = ''): Boolean;
var
  Form: TProfileEditorForm;
begin
  Form := TProfileEditorForm.Create(nil);
  try
    Form.FSetOptimalSize := not RestoreFormPosition(Form);
    Form.Prepare(Profile, OkCaption);
    Result := ShowModalForm(Form) = mrOk;
  finally
    Form.Free;
  end;
end;

end.

